





function getBindingAttr(
    el, name, getStatic
) {

    var dynamicValue =
        getAndRemoveAttr(el, ':' + name) ||
        getAndRemoveAttr(el, 'v-bind:' + name);

    if (dynamicValue != null) {
        return parseFilters(dynamicValue)

    } else if (getStatic !== false) {

        var staticValue = getAndRemoveAttr(el, name);

        if (staticValue != null) {
            return JSON.stringify(staticValue)
        }
    }
}



function addProp(el, name, value) {
    (el.props || (el.props = [])).push({ name: name, value: value });
    el.plain = false;
}

function addAttr(el, name, value) {
    (el.attrs || (el.attrs = [])).push({ name: name, value: value });
    el.plain = false;
}

// add a raw attr (use this in preTransforms)
function addRawAttr(el, name, value) {
    el.attrsMap[name] = value;
    el.attrsList.push({ name: name, value: value });
}

function addDirective(
    el, name, rawName, value,
    arg, modifiers
) {
    (el.directives || (el.directives = [])).push({ name: name, rawName: rawName, value: value, arg: arg, modifiers: modifiers });
    el.plain = false;
}

function addHandler(
    el, name,  value, modifiers,
    important, warn
) {

    modifiers = modifiers || emptyObject;

    // check capture modifier
    if (modifiers.capture) {
        delete modifiers.capture;
        name = '!' + name; // mark the event as captured
    }

    if (modifiers.once) {
        delete modifiers.once;
        name = '~' + name; // mark the event as once
    }

    /* istanbul ignore if */
    if (modifiers.passive) {
        delete modifiers.passive;
        name = '&' + name; // mark the event as passive
    }

    // normalize click.right and click.middle since they don't actually fire
    // this is technically browser-specific, but at least for now browsers are
    // the only target envs that have right/middle clicks.
    if (name === 'click') {

        if (modifiers.right) {
            name = 'contextmenu';
            delete modifiers.right;
        } else if (modifiers.middle) {
            name = 'mouseup';
        }

    }

    var events;

    if (modifiers.native) {
        delete modifiers.native;
        events = el.nativeEvents || (el.nativeEvents = {});
    } else {
        events = el.events || (el.events = {});
    }

    // g：添加回调，value 是回调的名字
    var newHandler = {
        value: value.trim()
    };

    if (modifiers !== emptyObject) {
        newHandler.modifiers = modifiers;
    }

    // g：以前的回调
    var handlers = events[name];

    // log_model('handlers',"name",name,"newHandler",newHandler,"el",el);

    /* istanbul ignore if */
    if (Array.isArray(handlers)) {
        important ? handlers.unshift(newHandler) : handlers.push(newHandler);
    } else if (handlers) {
        events[name] = important ? [newHandler, handlers] : [handlers, newHandler];
    } else {
        events[name] = newHandler;
    }

    // log_model("events",events)

    el.plain = false;
}

/******
 *  g：解析指令 ， 会把 这个指令从 template 中 移除，这样好看一些 
 * **/
function processPre(el) {

    // g：v-pre 的作用是
    // g：跳过指定元素的编译过程，可以显示原始数据标识符
    // g：设置了 v-pre 之后，元素上有 v-if 也不会进行解析
    if (getAndRemoveAttr(el, 'v-pre') != null) {
        el.pre = true;
    }
}

// g：保存静态属性 进attr
function processRawAttrs(el) {

    var l = el.attrsList.length;

    if (l) {

        var attrs = el.attrs = new Array(l);

        for (var i = 0; i < l; i++) {

            attrs[i] = {
                name: el.attrsList[i].name,
                value: JSON.stringify(el.attrsList[i].value)
            };
        }

    } else if (!el.pre) {

        // non root node in pre blocks with no attributes
        el.plain = true;
    }
}

function processElement(element, options) {

    processKey(element);

    // determine whether this is a plain element after
    // removing structural attributes
    element.plain = !element.key && !element.attrsList.length;

    processRef(element);
    processSlot(element);
    processComponent(element);

    for (var i = 0; i < transforms.length; i++) {
        element = transforms[i](element, options) || element;
    }

    processAttrs(element);
}

function processKey(el) {

    var exp = getBindingAttr(el, 'key');

    if (exp) {

        el.key = exp;
    }
}

function processRef(el) {

    var ref = getBindingAttr(el, 'ref');

    if (ref) {
        el.ref = ref;
        el.refInFor = checkInFor(el);
    }
}

function processFor(el) {

    var exp;

    if ((exp = getAndRemoveAttr(el, 'v-for'))) {

        var res = parseFor(exp);
        
        console.log("exp",res)


        if (res) {

            // g：把 res 对象的属性 混进 el 的对象中
            // g：res = {for: "items", alias: "a", iterator1: "index"}
            extend(el, res);
        } 
    }
}

function parseFor(exp) {

    // g：inMatch = ["(a,index) in items", "(a,index)", "items"]
    var inMatch = exp.match(forAliasRE);

    if (!inMatch) { return }

    var res = {};

    res.for = inMatch[2].trim();

    // g：alias = "a,index"
    var alias = inMatch[1].trim().replace(stripParensRE, '');

    // g：iteratorMatch =  [",index", "index"]
    var iteratorMatch = alias.match(forIteratorRE);

    if (iteratorMatch) {

        res.alias = alias.replace(forIteratorRE, '');

        // g：iteratorMatch[1] = index
        res.iterator1 = iteratorMatch[1].trim();

        if (iteratorMatch[2]) {
            res.iterator2 = iteratorMatch[2].trim();
        }

    } else {
        res.alias = alias;
    }

    // g：{for: "items", alias: "a", iterator1: "index"}
    return res
}

function processIf(el) {

    var exp = getAndRemoveAttr(el, 'v-if');

    if (exp) {
        el.if = exp;
        addIfCondition(el, {
            exp: exp,
            block: el
        });

    } else {
        if (getAndRemoveAttr(el, 'v-else') != null) {
            el.else = true;
        }
        var elseif = getAndRemoveAttr(el, 'v-else-if');
        if (elseif) {
            el.elseif = elseif;
        }
    }
}

function processIfConditions(el, parent) {

    var prev = findPrevElement(parent.children);

    console.log("preve",prev)

    if (prev && prev.if) {
        addIfCondition(prev, {
            exp: el.elseif,
            block: el
        });

    } 
}

function findPrevElement(children) {

    var i = children.length;

    while (i--) {

        if (children[i].type === 1) {
            return children[i]

        } else {

            children.pop();

        }
    }
}

function addIfCondition(el, condition) {

    if (!el.ifConditions) {
        el.ifConditions = [];
    }

    el.ifConditions.push(condition);
}

function processOnce(el) {

    var once$$1 = getAndRemoveAttr(el, 'v-once');

    if (once$$1 != null) {
        el.once = true;
    }
}

function processSlot(el) {

    if (el.tag === 'slot') {

        el.slotName = getBindingAttr(el, 'name');


    } else {

        var slotScope;

        if (el.tag === 'template') {
            slotScope = getAndRemoveAttr(el, 'scope');


            el.slotScope = slotScope || getAndRemoveAttr(el, 'slot-scope');

        } else if ((slotScope = getAndRemoveAttr(el, 'slot-scope'))) {


            el.slotScope = slotScope;

        }

        var slotTarget = getBindingAttr(el, 'slot');

        // console.log("el.tag !== 'template' && !el.slotScope",el.tag !== 'template' && !el.slotScope)

        if (slotTarget) {
            el.slotTarget = slotTarget === '""' ? '"default"' : slotTarget;

            // preserve slot as an attribute for native shadow DOM compat
            // only for non-scoped slots.
            // 保留slot作为本地 shadow DOM 兼容的属性，仅用于非作用域的slot。
            if (el.tag !== 'template' && !el.slotScope) {
                addAttr(el, 'slot', slotTarget);
            }

        }
    }
}

function processComponent(el) {

    var binding;

    if ((binding = getBindingAttr(el, 'is'))) {
        el.component = binding;
    }

    if (getAndRemoveAttr(el, 'inline-template') != null) {
        el.inlineTemplate = true;
    }
}

// g：开始解析属性
function processAttrs(el) {

    var list = el.attrsList;

    var i, l, name, rawName, value, modifiers, isProp;

    for (i = 0, l = list.length; i < l; i++) {
        
        name = rawName = list[i].name;
        value = list[i].value;

        // g：判断属性是否带有 'v-' , '@' , '：'
        if (dirRE.test(name)) {

            // mark element as dynamic
            el.hasBindings = true;

            
            // parseLog("我是正常的指令",name,name.replace(dirRE, ''))

            // modifiers
            // g：判断属性的绑定有没有传入值，比如 v-bind.a = "xxzxxxx"
            modifiers = parseModifiers(name);

            if (modifiers) {
                name = name.replace(modifierRE, '');
            }

            // g：v-bind，可能是绑定的属性，可能是传入子组件的props
            // g：bindRE = /^:|^v-bind:/
            if (bindRE.test(name)) { 

                name = name.replace(bindRE, '');
                value = parseFilters(value);
                isProp = false;

                if (modifiers) {

                    if (modifiers.prop) {
                        isProp = true;

                        name = camelize(name);
                        if (name === 'innerHtml') { name = 'innerHTML'; }
                    }

                    if (modifiers.camel) {
                        name = camelize(name);
                    }

                    if (modifiers.sync) {
                        addHandler(
                            el,
                            ("update:" + (camelize(name))),
                            genAssignmentCode(value, "$event")
                        );
                    }
                }

                if (isProp || (!el.component && platformMustUseProp(el.tag, el.attrsMap.type, name))) {
                    addProp(el, name, value);
                } else {
                    addAttr(el, name, value);
                }

            }
            
            // g：v-on
            // g：onRE = /^@|^v-on:/
            else if (onRE.test(name)) {

                name = name.replace(onRE, '');
                addHandler(el, name, value, modifiers, false);

            }
            
            // g：其他指令，比如 "v-once"，"@change" ，":"
            else { // normal directives

                // parseLog("我是正常的指令",name,name.replace(dirRE, ''))
                // g：把v- 去掉，拿到真正的 指令名字
                name = name.replace(dirRE, '');

                // g：parse arg 参数：比如 v-model:key = 5中的 key
                var argMatch = name.match(argRE);
                var arg = argMatch && argMatch[1];

                if (arg) {
                    name = name.slice(0, -(arg.length + 1));
                }

                addDirective(el, name, rawName, value, arg, modifiers);

                if ("development" !== 'production' && name === 'model') {
                    checkForAliasModel(el, value);
                }

            }


        } else {

            // literal attribute
            {   // g：检测普通属性，不能是 双括号的 比如 <div text="{{a}}" ></div> 如果是这样就报错
                var res = parseText(value);
            }

            addAttr(el, name, JSON.stringify(value));

            // #6887 firefox doesn't update muted state if set via attribute
            // even immediately after element creation

            if (!el.component &&
                name === 'muted' &&
                platformMustUseProp(el.tag, el.attrsMap.type, name)
            ) {
                addProp(el, name, 'true');
            }

        }
    }
}

function checkInFor(el) {

    var parent = el;

    while (parent) {
        if (parent.for !== undefined) {
            return true
        }
        parent = parent.parent;
    }

    return false
}

function parseModifiers(name) {

    var match = name.match(modifierRE);

    if (match) {
        var ret = {};
        match.forEach(function(m) { ret[m.slice(1)] = true; });
        return ret
    }
}


function parseFilters(exp) {

    var inSingle = false;
    var inDouble = false;
    var inTemplateString = false;
    var inRegex = false;
    var curly = 0;
    var square = 0;
    var paren = 0;
    var lastFilterIndex = 0;
    var c, prev, i, expression, filters;

    for (i = 0; i < exp.length; i++) {

        prev = c;

        c = exp.charCodeAt(i);

        if (inSingle) {
            if (c === 0x27 && prev !== 0x5C) { inSingle = false; }

        } else if (inDouble) {
            if (c === 0x22 && prev !== 0x5C) { inDouble = false; }

        } else if (inTemplateString) {
            if (c === 0x60 && prev !== 0x5C) { inTemplateString = false; }

        } else if (inRegex) {
            if (c === 0x2f && prev !== 0x5C) { inRegex = false; }

        } else if (

            c === 0x7C && // pipe
            exp.charCodeAt(i + 1) !== 0x7C &&
            exp.charCodeAt(i - 1) !== 0x7C &&
            !curly && !square && !paren

        ) {

            if (expression === undefined) {
                // first filter, end of expression
                lastFilterIndex = i + 1;
                expression = exp.slice(0, i).trim();
            } else {
                pushFilter();
            }

        } else {

            switch (c) {
                case 0x22:
                    inDouble = true;
                    break // "
                case 0x27:
                    inSingle = true;
                    break // '
                case 0x60:
                    inTemplateString = true;
                    break // `
                case 0x28:
                    paren++;
                    break // (
                case 0x29:
                    paren--;
                    break // )
                case 0x5B:
                    square++;
                    break // [
                case 0x5D:
                    square--;
                    break // ]
                case 0x7B:
                    curly++;
                    break // {
                case 0x7D:
                    curly--;
                    break // }
            }

            if (c === 0x2f) { // /

                var j = i - 1;
                var p = (void 0);

                // find first non-whitespace prev char
                for (; j >= 0; j--) {
                    p = exp.charAt(j);
                    if (p !== ' ') { break }
                }

                if (!p || !validDivisionCharRE.test(p)) {
                    inRegex = true;
                }

            }
        }
    }

    if (expression === undefined) {
        expression = exp.slice(0, i).trim();

    } else if (lastFilterIndex !== 0) {

        pushFilter();
    }

    function pushFilter() {
        (filters || (filters = [])).push(exp.slice(lastFilterIndex, i).trim());
        lastFilterIndex = i + 1;
    }

    if (filters) {
        for (i = 0; i < filters.length; i++) {
            expression = wrapFilter(expression, filters[i]);
        }
    }

    return expression
}

function wrapFilter(exp, filter) {

    var i = filter.indexOf('(');

    if (i < 0) {
        // _f: resolveFilter
        return ("_f(\"" + filter + "\")(" + exp + ")")
    } else {
        var name = filter.slice(0, i);
        var args = filter.slice(i + 1);
        return ("_f(\"" + name + "\")(" + exp + (args !== ')' ? ',' + args : args))
    }
}

function getAndRemoveAttr(
    el,  name,  removeFromMap
) {

    var val;

    if ((val = el.attrsMap[name]) != null) {

        var list = el.attrsList;

        for (var i = 0, l = list.length; i < l; i++) {
            if (list[i].name === name) {
                list.splice(i, 1);
                break
            }
        }

    }

    if (removeFromMap) {
        delete el.attrsMap[name];
    }
    return val
}
